package gov.fnal.xpath2ldap.util;

import org.apache.commons.cli.*;

import java.util.Locale;
import java.util.List;
import java.util.Properties;
import java.util.ArrayList;
import java.util.jar.JarFile;
import java.io.*;
import java.net.*;

/**
 * User: tdh
 * Date: Mar 28, 2009
 * Time: 11:28:10 AM
 */
public class Configuration {

    public static CommandLine processArguments(String[] args, Options options, String version, String syntax, String header, String footer) {

        CommandLineParser parser = new PosixParser();

        CommandLine line = null;
        try {
            // parse the arguments up until the first
            // command, then let the rest fall into
            // the arguments array.
            line = parser.parse(options, args, false);

            // Get and remove the commandName (first arg)
            //List<String> list = line.getArgList();
            //if (list.size() > 0){
            //    commandName = list.get(0);
            //    list.remove(0);
            //}

            // The rest of the args will be passed to the command
        } catch (ParseException exp) {
            exp.printStackTrace();
            System.exit(1);
        }

        if (line.hasOption("version")) {
            //Version.get().print(System.out);
            System.out.println(version);
            System.exit(1);
        }

        if (line.hasOption("help")) {
            help(options, syntax, header, footer);
            System.exit(1);
        }

        return line;
    }
     /*
     Properties props = null;
     try {
         props = findProperties(commandName, urls);
     } catch (IOException e1) {
         System.out.println("Unavailable command: " + commandName);

         help(options);

         System.exit(1);
     }


     if (props == null) {
         System.out.println("Unavailable command: " + commandName);
         help(options);
         System.exit(1);
     }
        */


    public static URL getModulePropertiesURL(String homeDirPropName/* = "xpathsearch.home"*/) throws IOException {
        return getModulePropertiesURL(homeDirPropName, "conf");
    }

    public static URL getModulePropertiesURL(String homeDirPropName, String subdir) throws IOException {
           //String homeDir = base + ".home";
        String homePath;
        File home=null;
        try {
            homePath = (String) System.getProperties().get(homeDirPropName);
            if (homePath == null) {
                return null;
            }

            home = new File(homePath);
            if (!home.exists() || (home.exists() && !home.isDirectory())) {
                throw new IOException("Cannot open " + homePath + " or is not a directory.");
            }

            home = home.getCanonicalFile();

        } catch (SecurityException e) {
        }

        //File dir = null;

        File conf = new File(home, subdir);
        //URL conf_url = new URL("file:/localhost/" + conf);
        //URL conf_url = new URL("file:/" + conf);
        URL conf_url = new URL("file://localhost" + conf + "/");
        return conf_url;
        /*try {
            dir = dir.getCanonicalFile();
            if (!dir.exists()) {
                try {
                    if (!dir.mkdirs())
                        throw new IOException("Cannot create the directory " + dir.getPath());
                } catch (SecurityException e) {
                    throw new IOException(
                            "Permission denied: Cannot create the directory " + dir.getPath() + " : " + e.getMessage());
                }
            } else if (dir.exists() && !dir.isDirectory()) {
                throw new IOException("The path specified is not a valid directory: " + dir.getPath());
            }
        } catch (IOException e) {
            System.out.println("Processing conf/system.properties failed: "+e.getMessage());
        }

           File conf = dir;
        */
    }

    public static void processModuleProperties(String propResName /*modulename.properties"*/, URL[] urls) throws IOException {

        // Read in and apply the modulename.properties
        try {
            Properties baseProperties = findProperties(propResName, urls);
            System.getProperties().putAll(baseProperties);
        } catch (IOException e) {
            System.err.println("Processing " + propResName + " failed: "+e.getMessage());
        }
        /*try {
            File file = new File(conf, "system.properties");
            if (file.exists()){
                Properties systemProperties = new Properties();
                FileInputStream fin = new FileInputStream(file);
                InputStream in = new BufferedInputStream(fin);
                systemProperties.load(in);
                System.getProperties().putAll(systemProperties);
                //env.putAll(systemProperties);
            }
        } catch (IOException e) {
            System.out.println("Processing conf/system.properties failed: "+e.getMessage());
        }

        try {
            File file = new File(conf, base + ".properties");
            if (file.exists()){
                Properties systemProperties = new Properties();
                FileInputStream fin = new FileInputStream(file);
                InputStream in = new BufferedInputStream(fin);
                systemProperties.load(in);
                System.getProperties().putAll(systemProperties);
                //env.putAll(systemProperties);
            }
        } catch (IOException e) {
            System.out.println("Processing conf/" + base + ".properties failed: "+e.getMessage());
        }
            */


        // Now read in and apply the properties specified on the command line
        /*for (int i = 0; i < args.length; i++) {
            String arg = args[i];
            if (arg.indexOf("-D") != -1) {
                String prop = arg.substring(arg.indexOf("-D") + 2, arg.indexOf("="));
                String val = arg.substring(arg.indexOf("=") + 1);

                System.setProperty(prop, val);
                //env.setProperty(prop, val);
            } else {
                argsList.add(arg);
            }
        }

        args = (String[]) argsList.toArray(new String[argsList.size()]);
        return args;*/
    }

    public static Properties findProperties(String uri, URL[] urls) throws IOException {
        String fulluri = uri;

        URL resource = getResource(fulluri, urls);
        if (resource == null) {
            throw new IOException("Could not find : " + fulluri);
        }

        return loadProperties(resource);
    }

    public static URL getResource(String fullUri, URL[] urls) {
        if (urls == null){
            ClassLoader cl = null;
            try {
                cl = ClassLoader.getSystemClassLoader();
            } catch (SecurityException ex) {}
            return cl.getResource(fullUri);
        }
        return findResource(fullUri, urls);
    }

    public static URL findResource(String resourceName, URL... search) {
        for (int i = 0; i < search.length; i++) {
            URL currentUrl = search[i];
            if (currentUrl == null) {
                continue;
            }
            JarFile jarFile = null;
            try {
                String protocol = currentUrl.getProtocol();
                {
                    URL resourceURL = targetURL(currentUrl, resourceName);
                    URLConnection urlConnection = resourceURL.openConnection();

                    try {
                        urlConnection.getInputStream().close();
                    } catch (SecurityException e) {
                        //return null;
                    }
                    // HTTP can return a stream on a non-existent file
                    // So check for the return code;
                    if (resourceURL.getProtocol().equals("http")) {
                       // return resourceURL;
                    //}

                    int code = ((HttpURLConnection) urlConnection).getResponseCode();
                    if (code >= 200 && code < 300) {
                        return resourceURL;
                    }
                    }
                }
                if (protocol.equals("file")) {
                    String baseFile = currentUrl.getFile();
                    String host = currentUrl.getHost();
                    int hostLength = 0;
                    if (host != null) {
                        hostLength = host.length();
                    }
                    StringBuffer buf = new StringBuffer(2 + hostLength + baseFile.length() + resourceName.length());

                    //if (hostLength > 0) {
                   //     buf.append("//").append(host);
                    //}
                    // baseFile always ends with '/'
                    buf.append(baseFile);
                    String fixedResName = resourceName;
                    // Do not create a UNC path, i.e. \\host
                    while (fixedResName.startsWith("/") || fixedResName.startsWith("\\")) {
                        fixedResName = fixedResName.substring(1);
                    }
                    buf.append(fixedResName);
                    String filename = buf.toString();
                    File file = new File(filename);
                    File file2 = new File(URLDecoder.decode(filename, "UTF-8"));

                    if (file.exists() || file2.exists()) {
                        return targetURL(currentUrl, fixedResName);
                    }
                } else if (protocol.equals("jar")) {
                    /*
                    * If the connection for currentUrl or resURL is
                    * used, getJarFile() will throw an exception if the
                    * entry doesn't exist.
                    */
                    URL jarURL = ((JarURLConnection) currentUrl.openConnection()).getJarFileURL();
                    try {
                        JarURLConnection juc = (JarURLConnection) new URL("jar", "", jarURL.toExternalForm() + "!/").openConnection();
                        jarFile = juc.getJarFile();
                    } catch (IOException e) {
                        // Don't look for this jar file again
                        search[i] = null;
                        throw e;
                    }

                    String entryName;
                    if (currentUrl.getFile().endsWith("!/")) {
                        entryName = resourceName;
                    } else {
                        String file = currentUrl.getFile();
                        int sepIdx = file.lastIndexOf("!/");
                        if (sepIdx == -1) {
                            // Invalid URL, don't look here again
                            search[i] = null;
                            continue;
                        }
                        sepIdx += 2;
                        StringBuffer sb = new StringBuffer(file.length() - sepIdx + resourceName.length());
                        sb.append(file.substring(sepIdx));
                        sb.append(resourceName);
                        entryName = sb.toString();
                    }
                    if (entryName.equals("META-INF/") && jarFile.getEntry("META-INF/MANIFEST.MF") != null){
                        return targetURL(currentUrl, "META-INF/MANIFEST.MF");
                    }
                    if (jarFile.getEntry(entryName) != null) {
                        return targetURL(currentUrl, resourceName);
                    }
                }
            } catch (MalformedURLException e) {
                // Keep iterating through the URL list
            } catch (IOException e) {
                Exception ex = e;
            } catch (SecurityException e) {
                Exception ex = e;
            }
        }
        return null;
    }

    public static URL targetURL(URL base, String name) throws MalformedURLException {
        StringBuffer sb = new StringBuffer(base.getFile().length() + name.length());
        sb.append(base.getFile());
        sb.append("/");
        sb.append(name);
        String file = sb.toString();
        return new URL(base.getProtocol(), base.getHost(), base.getPort(), file, null);
    }

    public static Properties loadProperties(URL resource) throws IOException {
        InputStream in = resource.openStream();

        BufferedInputStream reader = null;
        try {
            reader = new BufferedInputStream(in);
            Properties properties = new Properties();
            properties.load(reader);

            return properties;
        } finally {
            try {
                in.close();
                reader.close();
            } catch (Exception e) {
            }
        }
    }

    private static void help(Options options, String syntax, String header, String footer) {
        help(options, syntax, header, footer, true);
    }
    private static void help(Options options, String syntax, String header, String footer, boolean printHeader) {

        // Here we are using commons-cli to create the list of available commands
            //Options options = new Options();

            /*
            ResourceFinder commandFinder = new ResourceFinder("META-INF");
            Map<String, Properties> commands = commandFinder.mapAvailableProperties("org.apache.openejb.cli");
            for (Map.Entry<String, Properties> command : commands.entrySet()) {
                if (command.getKey().contains(".")) continue;
                Properties p = command.getValue();

                String description = p.getProperty(descriptionI18n, p.getProperty(descriptionBase));
                options.addOption(command.getKey(), false, description);
            }
            */
            HelpFormatter formatter = new HelpFormatter();
            StringWriter sw = new StringWriter();
            PrintWriter pw = new PrintWriter(sw);

            if (!printHeader){
                pw.append(header).append("\n\n");
                formatter.printOptions(pw, 74, options, 1, 3);
            } else {
                formatter.printHelp(pw, 74, syntax, header, options, 1, 3, footer, false);
            }

            pw.flush();
       /*
            // Fix up the commons-cli output to our liking.
            String text = sw.toString().replaceAll("\n -", "\n  ");
            text = text.replace("\nApache OpenEJB","\n\nApache OpenEJB");
            System.out.print(text);
        */
        System.out.print(sw);

    }
  /**
   * Modify configuration according user-specified generic options
   * @param conf Configuration to be modified
   * @param line User-specified generic options
   */
  /*
  private void processGeneralOptions(Configuration conf,
      CommandLine line) {
    if (line.hasOption("fs")) {
      FileSystem.setDefaultUri(conf, line.getOptionValue("fs"));
    }

    if (line.hasOption("jt")) {
      conf.set("mapred.job.tracker", line.getOptionValue("jt"));
    }
    if (line.hasOption("conf")) {
      conf.addResource(new Path(line.getOptionValue("conf")));
    }
    try {
      if (line.hasOption("libjars")) {
        conf.set("tmpjars",
                 validateFiles(line.getOptionValue("libjars"), conf));
      }
      if (line.hasOption("files")) {
        conf.set("tmpfiles",
                 validateFiles(line.getOptionValue("files"), conf));
      }
      if (line.hasOption("archives")) {
        conf.set("tmparchives",
                  validateFiles(line.getOptionValue("archives"), conf));
      }
    } catch (IOException ioe) {
      System.err.println(StringUtils.stringifyException(ioe));
    }
    if (line.hasOption('D')) {
      String[] property = line.getOptionValues('D');
      for(int i=0; i<property.length-1; i=i+2) {
        if (property[i]!=null)
          conf.set(property[i], property[i+1]);
      }
    }
  }
  */
    /*
            PrintWriter writer = new PrintWriter(System.out);

        if (line.hasOption('h')) {
            HelpFormatter formatter = new HelpFormatter();
            formatter.printHelp(
                writer,
                80, // width
                "groovysh [options]",
                "",
                options,
                4, // left pad
                4, // desc pad
                "",
                false); // auto usage

            writer.flush();
            System.exit(0);
        }
     */

    /*
        CommandLine line = parseCmdLine( args );

    boolean isUsingThreads = ( line.hasOption( "s" ) ) ? false : true;
    String destination = line.getOptionValue( "d" );
    String user = line.getOptionValue( "u" );
    String psswd = line.getOptionValue( "p" );
    String cfFile = line.getOptionValue( "c" );
    String topic = line.getOptionValue( "t" );

    if ( dbglog.isDebugEnabled() ) {
      dbglog.debug( "isUsingThread=" + isUsingThreads );
      dbglog.debug( "destination=" + destination );
      dbglog.debug( "user=" + user );
      dbglog.debug( "psswd=" + psswd );
      dbglog.debug( "cfFile=" + cfFile );
      dbglog.debug( "topic=" + topic );
    }

    SimpleJMSListener listener =
      new SimpleJMSListener( createConnectorMap( topic ), createCFMap( cfFile ),
                             destination, user, psswd, isUsingThreads );

    listener.start();
  }

  /**
   * Parse the command line options using commons-cli
   */
  /* static private CommandLine parseCmdLine (String[] args)
    throws ParseException
  {
    if ( dbglog.isDebugEnabled() ) {
      if ( args != null ) {
        for (int i=0; i < args.length; i++)
          dbglog.debug("Args #"+i+" ["+args [i]+"]");
      }
    }

    // create Options object
    options = new Options();
    options.addOption( createArgOption( "filename", "c", "connection factory properties filename" ));
    options.addOption( createArgOption( "destination", "d", "destination name" ));
    options.addOption( createArgOption( "topic", "t", "topic [absence of -t indicates queue]" ));
    options.addOption( createArgOption( "username", "u", "username" ));
    options.addOption( createArgOption( "password", "p", "password" ));
    options.addOption( new Option( "s", " single-threaded listener [absence of option => multithreaded]" ));

    try {
      // create the parser
      CommandLineParser parser = new PosixParser();

      String[] wargs = args;
      if ( wargs == null ) {
        wargs = new String[1];
        wargs[0] = "";
      }

      // parse the command line arguments
      //    NOTE: CLI cannot ignore unknown options that start with "-" !!!!!
      boolean stopAtNonOption = false;
      CommandLine line = parser.parse( options, wargs, stopAtNonOption );

      if ( dbglog.isDebugEnabled() ) {
        dbglog.debug("Printing all options");
        java.util.Iterator iterator = line.iterator ();
        while (iterator.hasNext ()) {
          dbglog.debug(" item ["+ iterator.next () +"]");
        }
        dbglog.debug("Parsing done. Remaining args ["+line.getArgList ()+"]");
      }

      return line;
    }
    catch( ParseException exp ) {
      // oops, something went wrong
      errlog.error( "Parsing failed.  Reason: " + exp.getMessage() );

      // automatically generate the help statement
      HelpFormatter formatter = new HelpFormatter();
      formatter.printHelp( "JMSListener", options );

      throw exp;
    }
  }
  */
  /**
   * <code>createOption</code> is a convenience method to create Option instance
   *
   * @param argName a <code>String</code> value
   * @param op a <code>String</code> value
   * @param desc a <code>String</code> value
   * @return an <code>Option</code> value
   */
  public static Option createArgOption (String argName, String op, String desc)
  {
    return OptionBuilder.withArgName( argName )
      .withDescription( desc )
      .hasArg ()
      .create( op );
  }

}
